import { createElement } from "./utils.mjs";


/**
 * 模版
 */
const TEMPLATE = (function () {
    const template = createElement("template");
    const styleElm = createElement("style", { "type": "text/css" }, [
        document.createTextNode(
            ":host{" +
            "display:inline-block;" +
            "min-width:300px;" +
            "min-height:150px;" +
            "}"
        )
    ]);
    template.content.appendChild(styleElm);
    template.content.appendChild(createElement("canvas", {
        "id": "canvas"
    }));
    return template;
})();

/**
 * 背景色
 */
const BG_COLOR = "#0f0f0f";
/**
 * 前景色
 */
const FG_COLOR = "#f0f0f0";
/**
 * 字体大小
 */
const FONT_SIZE = 16;
/**
 * 行高
 */
const LINE_HEIGHT = FONT_SIZE + 2;
/**
 * 实心光标
 */
const CURSOR_1 = "\u25AE";
/**
 * 空心光标
 */
const CURSOR_0 = "\u25AF";

/**
 * 字母键
 */
const CHAR_KEYS = [
    "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", 
    "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
    "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", 
    "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
];
/**
 * 数字键
 */
const NUM_KEYS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
/**
 * 符号键
 */
const SYMB_KEYS = [" ", "~", "$", "*", "-", "/", ":", "_"];

/**
 * 键盘事件
 */
export class KeyEvent {
    /**
     * 构造函数
     * @param {string} data 
     * @param {string} code 
     * @param {boolean} ctrlKey 
     * @param {boolean} shiftKey 
     * @param {boolean} altKey 
     * @param {boolean} metaKey
     */
    constructor(data, code, ctrlKey, shiftKey, altKey, metaKey) {
        this.data = data;
        this.code = code;
        this.ctrlKey = ctrlKey;
        this.shiftKey = shiftKey;
        this.altKey = altKey;
        this.metaKey = metaKey;
    }
}

/**
 * 终端元素
 */
export class LogicTerminal extends HTMLElement {
    /**
     * 构造函数
     */
    constructor(width = 300, height = 150) {
        super()

        const htmlNode = TEMPLATE.content.cloneNode(true);
        // 创建内部根节点
        this._shadowRoot = this.attachShadow({
            mode: "closed"
        });
        this._shadowRoot.appendChild(htmlNode);
        /**
         * 按键监听器集合
         * @type {Set<(e: KeyEvent) => void>}
         */
        this._keyEventListeners = new Set();
        /**
         * 内容
         * @type {string}
         */
        this._content = "";
        /**
         * 输入流
         */
        this.input = {
            /**
             * 添加键盘事件监听器
             * @param {(e: KeyEvent) => void} callback 
             */
            addKeyPressListener: (callback) => {
                this._keyEventListeners.add(callback);
            },
            /**
             * 移除键盘事件监听器
             * @param {(e: KeyEvent) => void} callback 
             */
            removeKeyPressListener: (callback) => {
                this._keyEventListeners.delete(callback);
            }
        };
        /**
         * 输出流
         */
        this.output = {
            /**
             * 清除
             */
            clear: () => {
                this._content = "";
                requestAnimationFrame(this._render);
            },
            /**
             * 写入
             * @param {string} content 
             */
            write: (content) => {
                this._content = content;
                requestAnimationFrame(this._render);
            },
            /**
             * 更新
             * @param {string} content 
             */
            update: (content) => {
                this._content = content;
                requestAnimationFrame(this._render);
            }
        };

        // /**
        //  * 按键代码字典
        //  * @type {Map<string, unknown>}
        //  */
        // this.keymap = new Map();

        /**
         * 画布对象
         */
        this._canvas = this._shadowRoot.querySelector("#canvas");
        if (!this._canvas) {
            throw new ReferenceError("The #canvas element cannot be found.");
        }
        // 设置tabindex=0，以使其可获得焦点
        this._canvas.setAttribute("tabindex", "0");

        /**
         * 是否获得焦点
         * @type {boolean}
         */
        this._focused = false;
        // 监听获得焦点事件
        this.addEventListener("focus", (event) => {
            this._focused = true;
            requestAnimationFrame(this._render);
        }, true);
        // 监听失去焦点事件
        this.addEventListener('blur', (event) => {
            this._focused = false;
            requestAnimationFrame(this._render);
        }, true);

        /**
         * 设置尺寸
         */
        this._width = 0;
        this._height = 0;
        this.setSize(width, height);
    }

    /**
     * 设置大小
     * @param {number} width 
     * @param {number} height 
     */
    setSize(width, height) {
        this._width = width;
        this._height = height;

        // 设置画布尺寸(同时适配高清屏幕)
        if (this._canvas) {
            // Set display size
            this._canvas.style.width = `${width}px`;
            this._canvas.style.height = `${height}px`;
            // Set actual size in memory (scaled to account for extra pixel density).
            // Change to 1 on retina screens to see blurry canvas.
            const scale = window.devicePixelRatio;
            this._canvas.width = Math.floor(width * scale);
            this._canvas.height = Math.floor(height * scale);
            // Normalize coordinate system to use css pixels.
            const ctx = this._canvas.getContext("2d");
            ctx.scale(scale, scale);
        }
    }

    /**
     * 宽度
     * @returns {number}
     */
    get width() {
        return this._width;
    }

    /**
     * 高度
     * @returns {number}
     */
    get height() {
        return this._height;
    }

    /**
     * 渲染(私有方法)
     */
    _render = () => {
        const ctx = this._canvas.getContext("2d");
        ctx.fillStyle = BG_COLOR;
        ctx.fillRect(0, 0, this._canvas.width, this._canvas.height);
        // 追加光标
        let content = this._content;
        if (this._focused) {
            content += CURSOR_1;
        } else {
            content += CURSOR_0;
        }
        // 设置字体和样式
        ctx.font = `${FONT_SIZE}px monospace`;
        ctx.textAlign = "start ";
        ctx.textBaseline = "top";
        ctx.fillStyle = FG_COLOR;
        // 绘制所有行
        const lineArr = content.split("\n");
        let yOffset = 4;
        for (const line of lineArr) {
            ctx.fillText(line, 4, yOffset);
            yOffset += LINE_HEIGHT;
            // 忽略超出部分
            if (yOffset > this._canvas.height) {
                break;
            }
        }
    };

    /**
     * 全局按键事件处理(私有)
     * @param {KeyboardEvent} e 
     */
    _onWindowKeyDown = (e) => {
        if (this._focused) {
            let key = e.key;
            if (CHAR_KEYS.indexOf(key) < 0 && NUM_KEYS.indexOf(key) < 0 && SYMB_KEYS.indexOf(key) < 0) {
                key = null;
            }
            // 事件转发
            const keyEvent = new KeyEvent(key, e.code, e.ctrlKey, e.shiftKey, e.altKey, e.metaKey);
            for (const listener of this._keyEventListeners) {
                listener.call(null, keyEvent);
            }
        }
    };

    connectedCallback() {
        // 绑定全局按键事件
        window.addEventListener("keydown", this._onWindowKeyDown);
        // 第一次渲染
        requestAnimationFrame(this._render);
    }

    disconnectedCallback() {
        // 解绑全局按键事件
        window.removeEventListener("keydown", this._onWindowKeyDown);
    }

    /**
     * 退出
     * @param {number} code 
     */
    exit(code) {
        // TODO::Nothing
    }
}

// 注册到WebComponent
customElements.define("logic-terminal", LogicTerminal);
